package com.harzone.commons.search.impl.statement;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.solr.client.solrj.SolrQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.harzone.commons.search.domain.Condition;
import com.harzone.commons.search.domain.KeyValue;
import com.harzone.commons.search.domain.MethodCondition;
import com.harzone.commons.search.domain.Where;
import com.harzone.commons.search.exception.SearchException;
import com.harzone.commons.search.impl.utils.SynchronizdDateUtil;
import com.harzone.commons.search.visitor.WhereVisitor;


/**
 * where条件转换器
 * @author ls
 * 2014年11月25日下午2:05:52
 */
public class WhereParser extends BaseParser implements WhereVisitor  {

	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private final String NOT_FLAG = "-";
	
	protected List<Object> list = new ArrayList<Object>(); 
	
	protected Where where = null;
	
	private boolean hasMethod = false;
	
	public WhereParser(Where where) {
		 this.where = where;
	}
	 
	
	public SolrQuery parse(SolrQuery query){
		this.visit(where);
		int index = 0;
		if(list.size()==1){
			Object object = list.get(0); 
			StringBuilder sb = new  StringBuilder();
			if(object instanceof List){
				this.makeMainWhere(object,query,sb,index++);
			}
		}
		if(hasMethod){
			String[] strs = query.getFilterQueries();
			StringBuilder sb = new StringBuilder();
			int index1 = 0;
			for (String string : strs) {
				if(index1>0){
					sb.append(" AND ");
				}
				sb.append(string);
				index1 ++;
			}
			query.setFilterQueries("");
			query.setQuery(sb.toString());
		}
		return query;
	}
	
	
	@Override
	public void visit(Where where) {
		if(where==null){
			return ;
		}
		if(!where.hasWhere()){
			if (where instanceof Condition) {
				this.visit((Condition) where);
			}else if(where instanceof MethodCondition) {
				this.visit((MethodCondition) where);
			}
		}else{
			List<Object> sublist = new ArrayList<Object>();
			list.add(sublist);
			sublist.add(where.getConn().name());
			for (Where nextsubWhere : where.getWheres()) {
				this.makeWhere(where,nextsubWhere,sublist);
			}
		}
	}

	@Override
	public void visit(Condition condition) {
		list.add(condition.getConn()+" "+this.makeWhereStr(condition));
	}
	
	@Override
	public void visit(MethodCondition condition) {
		list.add(condition.getConn()+" "+this.makeMethodStr(condition));
		hasMethod = true;
	}
	
	private String makeMethodStr(MethodCondition condition){
		StringBuilder sb = new StringBuilder();
		sb.append("_val_:");
		sb.append("\"");
		//sb.append("if(");
		sb.append(this.handlMethodCondition(condition));
		//sb.append(",true,false)");
		sb.append("\"");
		return sb.toString();
	}
	
	
	private String handlMethodCondition(final MethodCondition condition){
		StringBuilder sb = new StringBuilder();
		sb.append(condition.getName());
		sb.append("(");
		List<KeyValue> list = condition.getParams();
		int index = 0;
		for (KeyValue keyValue : list) {
			if(index > 0){
				sb.append(",");
			}
			sb.append(keyValue.value);
			index++;
		}
		sb.append(")");
		return sb.toString();
	}
	
	/**
	 * 处理where
	 * @author ls
	 * 2014年11月24日下午2:38:26 
	 * @param where
	 * @param subWhere
	 * @param list
	 */
	private void makeWhere(final Where where, final Where subWhere
			, final List<Object> list) {
		if(subWhere==null){
			return;
		}
		if(!subWhere.hasWhere()){
			if (subWhere instanceof Condition) {
				list.add(((Condition) subWhere).getConn()+" "+this.makeWhereStr((Condition)subWhere));
			}else if(subWhere instanceof MethodCondition) {
				list.add(((MethodCondition) subWhere).getConn()+" "+this.makeMethodStr((MethodCondition)subWhere));
				hasMethod = true;
			}
		}else{
			//新的子查询
			List<Object> sublist = new ArrayList<Object>();
				list.add(sublist);
			sublist.add(subWhere.getConn().name());
			for (Where nextsubWhere : subWhere.getWheres()) {
				this.makeWhere(subWhere,nextsubWhere,sublist);
			}
		}
	}
	
	
	/**
	 * 处理where
	 * @author ls
	 * 2014年11月24日下午2:37:59 
	 * @param object
	 * @param query
	 * @param sb
	 * @param index
	 */
	private void makeMainWhere(Object object,SolrQuery query,StringBuilder sb,int index ){
		if(object instanceof List){
			List<Object> list = (List)object;
			int subWhereIndex = 0;
			boolean and = true, hasSub = false;
			Object subobject = null;
			for (Object object2 : list) {
				StringBuilder sb1 = new  StringBuilder();
				subobject = object2;
				subWhereIndex++;
				if(subWhereIndex==1){
					if(subobject instanceof String){
						if(subobject.toString().startsWith("OR")){
							and = false;
						}
						if(!and){
							sb.append("(");
						}
					}
					continue;
				}else if(subWhereIndex==2){
					if(subobject instanceof String){
						if(subobject.toString().startsWith("AND")){
							subobject = subobject.toString().replace("AND ", "");
						}else{
							subobject = subobject.toString().replace("OR ", "");
						}
					} 
				}else{
					if(subobject instanceof String){
						subobject = " "+subobject;
					}
				}
				boolean has = this.makeSubQuery(subobject, sb1, and, subWhereIndex);
				if(has){
					hasSub = has;
				}
				String queryStr = sb1.toString();
				if(queryStr.startsWith(" AND ")){
					int index1 = queryStr.indexOf(" AND ");
					queryStr = queryStr.substring(index1+" AND ".length());
				}
				if(and){
					logger.info(queryStr);
					query.addFilterQuery(queryStr);
				}else{
					sb.append(queryStr);
				}
			}
			if(!and){ 
				if(!hasSub){
					sb.append(")");
				}
				logger.info(sb.toString());
				query.addFilterQuery(sb.toString());
			}
		} 
	}
	
	/**
	 * 处理子查询
	 * @author ls
	 * 2014年11月24日下午2:37:47 
	 * @param object
	 * @param sb
	 * @param parentAnd
	 * @param index
	 */
	private boolean makeSubQuery(Object object,StringBuilder sb,boolean parentAnd,int index ){
		boolean has = false;
		if(object instanceof List){
			has = true;
			List<Object> list = (List)object;
			StringBuilder subsb = new  StringBuilder();
			int subWhereIndex = 0;
			boolean suband = true;
			Object subobject = null;
			for (Object object2 : list) {
				subobject = object2;
				subWhereIndex++;
				if(subWhereIndex==1){
					if(subobject instanceof String){
						if(subobject.toString().startsWith("OR")){
							suband = false;
						}
						if(parentAnd){
							subsb.append(" AND ");
						}else {
							subsb.append(" OR ");
						}
						if(suband!=parentAnd){
							subsb.append("(");
						}
					}
					continue;
				}else if(subWhereIndex==2){
					if(subobject instanceof String){
						if(subobject.toString().startsWith("AND")){
							subobject = subobject.toString().replace("AND ", "");
						}else{
							subobject = subobject.toString().replace("OR ", "");
						}
					} 
				}else{
					if(subobject instanceof String){
						subobject = " "+subobject;
					}
				}
				this.makeSubQuery(subobject, subsb, suband, subWhereIndex);
			}
			if(subWhereIndex > 0 && !suband){
				subsb.append(")");
			}else{
				if(suband!=parentAnd){
					subsb.append(")");
				}
			}
			sb.append(subsb);
		}else{ 
			String queryStr = object.toString();
			if(!parentAnd){
				queryStr = queryStr.replaceAll("AND", "OR");
			}else{
				queryStr = queryStr.replaceAll("OR", "AND");
			}
			sb.append(queryStr);
		}
		return has;
	}
	 

	/**
	 * 条件字符串
	 * @author ls
	 * 2014年11月24日下午2:37:22 
	 * @param condition
	 * @return
	 */
	private String makeWhereStr(final Condition condition){
		StringBuilder sb = new StringBuilder();
		if(condition != null){
			Condition.OPEAR op = condition.getOpear();
			String value = "*"; 
			String fieldBeforeFlag = "";
			boolean valid = true;
			switch(op){
				case BETWEEN:{
						value = this.makeBetween(condition);
				    }
					break;
				case NBETWEEN:{
					    fieldBeforeFlag = NOT_FLAG;
						value = this.makeBetween(condition);
			    	}
					break;
				case IS:
				case EQ1:
				case EQ:
					if(condition.getValue() instanceof Date){
						value = this.mankRange(condition.getValue(), condition.getValue());
					}else{
						value = this.getValue(condition.getValue());
					}
					break;
				case ISN:
				case NEQ: 
				case N: 
					fieldBeforeFlag = NOT_FLAG;
					if(condition.getValue() instanceof Date){
						value = this.mankRange(condition.getValue(), condition.getValue());
					}else{
						value = this.getValue(condition.getValue());
					}
					break;
				case GT:
					value = this.mankRange("{", condition.getValue(), null, "]");
					break;
				case GTE:
					value = this.mankRange("[", condition.getValue(), null, "]");
					break;
				case LT:
					value = this.mankRange("[", null, condition.getValue(), "}");
					break;
				case LTE:
					value = this.mankRange("[", null, condition.getValue(), "]");
					break;
				case IN:
					value = this.makeIn(condition);
					break;
				case NIN:
					fieldBeforeFlag = NOT_FLAG;
					value = this.makeIn(condition);
					break;
				case LIKE:
					value = this.makeLike(condition);
					break;
				case NLIKE:
					fieldBeforeFlag = NOT_FLAG;
					value = this.makeLike(condition);
					break;
				case UNKOWN:
				default:
					valid = false;
					throw new SearchException("暂不支持该类型的操作：" + op.getName());
			}
			if(valid){
				sb.append(fieldBeforeFlag);
				sb.append(condition.getName());
				sb.append(":");
				sb.append(value);
			}
		}
		return sb.toString();
	}
	
	
	private String makeLike(final Condition condition){
		Object value = condition.getValue();
		value = this.getValue(value);
		String v = String.valueOf(value);
//		v = ((String) value).replace('%', '*');
//		.replace('_', '?').replace('-', '?');
		if(v.startsWith("%")){
			v = "*" + v.substring(v.indexOf("%")+1);
		}
		if(v.endsWith("%")){
			v =  v.substring(0,v.lastIndexOf("%")) + "*";
		}
		return v;
	}
	
	
	private String makeIn(final Condition condition) {
		String v = "";
		Object value = condition.getValue();
		if(value instanceof Object[]){
			Object[] values = (Object[])value;
			v =makeIn(values);
		}else if(value instanceof Collection){
			Collection values = (Collection)value;
			v = makeIn(values);
		}else{
			v = makeIn(value);
		}
		return v;
	}
	
	
	private String makeIn(Object... values) {
		StringBuilder sb = new StringBuilder();
		if (values != null) {
			int i = 0;
			for (Object string : values) {
				string = this.getValue(string);
				if (string != null && !"".equals(string)) {
					if (i == 0) {
						sb.append("(");
					}
					if (i > 0) {
						sb.append(" ");
					}
					sb.append(string);
					i++;
				}
			}
			if (i > 0) {
				sb.append(")");
			}
		}
		return sb.toString();
	}
	
	private String makeIn(Collection<Object> values) {
		StringBuilder sb = new StringBuilder();
		if (values != null) {
			int i = 0;
			for (Object string : values) {
				string = this.getValue(string);
				if (string != null && !"".equals(string)) {
					if (i == 0) {
						sb.append("(");
					}
					if (i > 0) {
						sb.append(" ");
					}
					sb.append(string);
					i++;
				}
			}
			if (i > 0) {
				sb.append(")");
			}else{
				sb.append("*");
			}
		}
		return sb.toString();
	}
	
	
	private String makeBetween(final Condition condition){
		Object v = condition.getValue();
		String value = getValue(v);
		Object v1 = condition.getValue1();
		String value1 = getValue(v1);
		return this.mankRange(value, value1);
	}
	
	
	private String getValue(Object v){
		String value = String.valueOf(v);
		if(v != null){
			if( v instanceof Date){
				value = SynchronizdDateUtil.getSolrDateFormat().format((Date)v);
			}else{
				value = this.escapeQueryChars(v.toString());
			}
		}else{
			value = "*";
		}
		return value;
	}
	
	
	private String mankRange(final Object value1, final Object value2){
		return this.mankRange("[", value1, value2, "]");
	}
	
	
	private String mankRange(final String startFlag, final Object value1, final Object value2, final String endFlag){
		StringBuilder sb = new StringBuilder(startFlag);
		if(value1==null || "".equals(value1)){
			sb.append("*");
		}else{
			sb.append(getValue(value1));
		}
		sb.append(" TO ");
		if(value2==null || "".equals(value2)){
			sb.append("*");
		}else{
			sb.append(getValue(value2));
		}
		sb.append(endFlag); 
		return sb.toString();
	}


	public boolean isHasMethod() {
		return hasMethod;
	}


	public void setHasMethod(boolean hasMethod) {
		this.hasMethod = hasMethod;
	}
}
